package com.ssm.basis.web.aop.lock;

import com.ssm.basis.service.exception.CustomException;
import com.ssm.basis.web.util.RequestUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Objects;

import static com.ssm.basis.service.constant.SecurityConstant.SUPPRESS_WARNING_UNCHECKED;

/**
 * https://github.com/lxchinesszz/tomato
 */
@Slf4j
@Aspect
// @Component
@RequiredArgsConstructor
class RepeatRequestAspect {
    static final String REPEAT_REQUEST_KEY_PREFIX = "repeatRequest:";
    final RedisTemplate redisTemplate;

    @Around("@annotation(repeatRequest) && (@annotation(org.springframework.web.bind.annotation.RequestMapping)" +
            " || @annotation(org.springframework.web.bind.annotation.GetMapping)" +
            " || @annotation(org.springframework.web.bind.annotation.PostMapping)" +
            " || @annotation(org.springframework.web.bind.annotation.DeleteMapping)" +
            " || @annotation(org.springframework.web.bind.annotation.PutMapping))")
    @SuppressWarnings(SUPPRESS_WARNING_UNCHECKED)
    public Object doAround(ProceedingJoinPoint pjp, RepeatRequest repeatRequest) throws Throwable {
        // 建议：注入 restTemplate 的时候不指定类型，而在获取处理器的时候手动指定泛型
        ValueOperations<String, Integer> valueOperations = redisTemplate.opsForValue();

        // 生成 key
        HttpServletRequest currentRequest = RequestUtil.getCurrentRequest();
        Object unique;
        if (!ObjectUtils.isEmpty(pjp.getArgs())) {
            unique = Arrays.hashCode(pjp.getArgs());
        } else if (Objects.nonNull(SecurityContextHolder.getContext())) {
            unique = ((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
        } else {
            return pjp.proceed();
        }
        String servletPath = currentRequest.getServletPath();
        String key = REPEAT_REQUEST_KEY_PREFIX.concat(unique.toString()).concat(":").concat(servletPath);

        if (Objects.nonNull(valueOperations.get(key))) {
            // 如果缓存中有这个 url，视为重复提交
            log.warn(repeatRequest.message());
            throw new CustomException("10030", repeatRequest.message());
        } else {
            valueOperations.set(key, 1, repeatRequest.lockTime(), repeatRequest.timeUnit());
            return pjp.proceed();
        }
    }
}
